package com.ruoyi.network.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.net.NetUtil;
import com.alibaba.excel.EasyExcel;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.network.domain.entity.IpRange;
import com.ruoyi.network.enums.BaseStatusEnum;
import com.ruoyi.network.mapper.IpRangeMapper;
import com.ruoyi.network.model.vo.IpRangeCountVO;
import com.ruoyi.network.model.vo.IpRangeExcelVO;
import com.ruoyi.network.model.vo.IpRangePageVO;
import com.ruoyi.network.model.vo.IpRangeRequestVO;
import com.ruoyi.network.service.IIpRangeService;
import com.ruoyi.network.service.listener.IpRangeListener;
import org.apache.logging.log4j.util.Strings;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

import static com.ruoyi.network.utils.IpRangeUtil.calculateIPRange;
import static com.ruoyi.network.utils.IpRangeUtil.isValidIpRangeInfo;

/**
 * IP段结构Service业务层处理
 *
 * @author lyp
 * @date 2024-12-05
 */
@Service
public class IpRangeServiceImpl extends ServiceImpl<IpRangeMapper, IpRange> implements IIpRangeService {
    @Resource
    private IpRangeMapper ipRangeMapper;

    /**
     * 查询IP段结构
     *
     * @param id IP段结构主键
     * @return IP段结构
     */
    @Override
    public IpRange selectIpRangeById(Long id) {
        return getBaseMapper().selectById(id);
    }

    /**
     * 查询IP段结构列表
     *
     * @param vo 分页参数
     * @return TableDataInfo 分页结果
     */
    @Override
    public TableDataInfo selectIpRangeList(IpRangePageVO vo) {
        Page<IpRange> page = new Page<>(vo.getPage(), vo.getSize());

        Page<IpRange> rangePage = getBaseMapper().selectPage(page, Wrappers.<IpRange>lambdaQuery()
                .eq(Strings.isNotBlank(vo.getRangeName()), IpRange::getRangeName, vo.getRangeName())
                .eq(Objects.nonNull(vo.getUsageBizId()), IpRange::getUsageBizId, vo.getUsageBizId())
                .eq(Objects.nonNull(vo.getUsageDeptId()), IpRange::getUsageDeptId, vo.getUsageDeptId())
        );
        if (rangePage.getTotal() == 0) {
            return new TableDataInfo(Collections.emptyList(), 0);
        }
        return new TableDataInfo(rangePage.getRecords(), Integer.parseInt(String.valueOf(rangePage.getTotal())));
    }

    /**
     * 构建网段基础信息
     *
     * @param vo 构建参数
     * @return IpRange 网段信息
     */
    private IpRange createIpRange(IpRangeRequestVO vo) {
        IpRange ipRange = new IpRange();
        ipRange.setRangeName(vo.getRangeName());
        ipRange.setIpMask(vo.getIpMask());
        ipRange.setAsgCount(vo.getAsgCount());
        ipRange.setUsageBizId(vo.getUsageBizId());
        ipRange.setUsageDeptId(vo.getUsageDeptId());
        ipRange.setNetworkIdentifier(vo.getNetworkIdentifier());
        ipRange.setStatus(BaseStatusEnum.ENABLE);
        ipRange.setFormatName(vo.getRangeKey());
        Map<Boolean, String> map = calculateIPRange(ipRange.getFormatName(), ipRange.getIpMask());
        ipRange.setStartIp(NetUtil.ipv4ToLong(Optional.ofNullable(map.get(true)).orElse("")));
        ipRange.setEndIp(NetUtil.ipv4ToLong(Optional.ofNullable(map.get(false)).orElse("")));
        return ipRange;
    }

    /**
     * 新增IP段结构
     *
     * @param vo IP段结构
     * @return 结果
     */
    @Override
    public int insertIpRange(IpRangeRequestVO vo) {
        isValidIpRangeInfo(vo);

        IpRange ipRangeInfo = findByIpRange(vo.getRangeName());
        if (ipRangeInfo != null) {
            throw new ServiceException("网段已存在");
        }

        IpRange ipRange = createIpRange(vo);
        return getBaseMapper().insert(ipRange);
    }

    /**
     * 通过网段表示查询网段信息
     *
     * @param rangeName 网段名称 192.168.1
     * @return IpRange 网段信息
     */
    private IpRange findByIpRange(String rangeName) {
        if (Strings.isBlank(rangeName)) {
            return null;
        }
        return getBaseMapper().selectOne(Wrappers.<IpRange>lambdaQuery()
                .eq(IpRange::getRangeName, rangeName)
                .last("LIMIT 1")
        );
    }

    /**
     * 修改IP段结构
     *
     * @param vo IP段结构
     * @return int 结果
     */
    @Override
    public int updateIpRange(IpRangeRequestVO vo) {
        IpRange ipRange = findById(vo.getId());
        if (ipRange == null) {
            throw new ServiceException("网段信息不存在");
        }

        // 校验主要信息是否发生变更
        if (!Objects.equals(ipRange.getRangeName(), vo.getRangeName()) ||
                !Objects.equals(ipRange.getIpMask(), vo.getIpMask()) ||
                !Objects.equals(ipRange.getUsageBizId(), vo.getUsageBizId()) ||
                !Objects.equals(ipRange.getUsageDeptId(), vo.getUsageDeptId())) {
            // TODO 通过ip段IP统计当前分配的数量 不存在分配可以进行编辑
            // long count = findRangeIdCount(ipRange.getId());
            return 0;
        }

        isValidIpRangeInfo(vo);

        if (!Objects.equals(ipRange.getRangeName(), vo.getRangeName())) {
            IpRange ipRangeInfo = findByIpRange(vo.getRangeName());
            if (ipRangeInfo != null && !Objects.equals(ipRangeInfo.getId(), ipRange.getId())) {
                throw new ServiceException("网段已存在");
            }
        }
        updateTpRangeEntity(ipRange, vo);
        return getBaseMapper().updateById(ipRange);
    }

    /**
     * 更新IP段实体
     *
     * @param ipRange ip段实体
     * @param vo      更新参数
     */
    private void updateTpRangeEntity(IpRange ipRange, IpRangeRequestVO vo) {
        ipRange.setRangeName(vo.getRangeName());
        ipRange.setIpMask(vo.getIpMask());
        ipRange.setUsageBizId(vo.getUsageBizId());
        ipRange.setUsageDeptId(vo.getUsageDeptId());
        ipRange.setNetworkIdentifier(vo.getNetworkIdentifier());
        ipRange.setAsgCount(vo.getAsgCount());
        ipRange.setFormatName(vo.getRangeKey());
        ipRange.setStatus(BaseStatusEnum.ENABLE);
        Map<Boolean, String> map = calculateIPRange(ipRange.getFormatName(), ipRange.getIpMask());
        ipRange.setStartIp(NetUtil.ipv4ToLong(Optional.ofNullable(map.get(true)).orElse("")));
        ipRange.setEndIp(NetUtil.ipv4ToLong(Optional.ofNullable(map.get(false)).orElse("")));
    }

    /**
     * 批量删除IP段结构
     *
     * @param ids 需要删除的IP段结构主键
     * @return 结果
     */
    @Override
    public int deleteIpRangeByIds(List<Long> ids) {
        if (CollUtil.isEmpty(ids)) {
            return 0;
        }
        return getBaseMapper().deleteBatchIds(ids);
    }

    /**
     * 删除IP段结构信息
     *
     * @param id IP段结构主键
     * @return 结果
     */
    @Override
    public int deleteIpRangeById(Long id) {
        return ipRangeMapper.deleteIpRangeById(id);
    }

    /**
     * 网段统计结果
     *
     * @param vo 统计参数VO
     * @return long 统计结果
     */
    @Override
    public long count(IpRangeCountVO vo) {
        return getBaseMapper().selectCount(Wrappers.<IpRange>lambdaQuery()
                .eq(Objects.nonNull(vo.getStatus()), IpRange::getStatus, vo.getStatus())
        );
    }

    /**
     * IP段数据导入
     *
     * @param file 带解析文件
     * @return Boolean 结果
     */
    @Override
    public Boolean importExcel(MultipartFile file) {
        try {
            EasyExcel.read(file.getInputStream(), IpRange.class, new IpRangeListener()).sheet().doRead();
        } catch (Exception e) {
            throw new ServiceException(e.getMessage());
        }
        return true;
    }

    /**
     * 校验IP段重复性
     *
     * @param collection 待校验数据
     * @return List<String> IP段列表
     */
    @Override
    public List<String> checkIpRangeUnique(Collection<String> collection) {
        if (CollUtil.isEmpty(collection)) {
            return Collections.emptyList();
        }

        return getBaseMapper().selectList(Wrappers.<IpRange>lambdaQuery()
                .select(IpRange::getRangeName)
                .in(IpRange::getRangeName, collection)
        ).stream().map(IpRange::getRangeName).collect(Collectors.toList());
    }

    /**
     * 批量插入
     *
     * @param rangeList 待入库数据
     * @return int 插入数量
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean batchInsertion(List<IpRange> rangeList) {
        return saveBatch(rangeList);
    }

    /**
     * 导出
     *
     * @param response 响应域
     * @param vo       查询参数
     */
    @Override
    public void export(HttpServletResponse response, IpRangePageVO vo) {
        TableDataInfo dataInfo = selectIpRangeList(vo);
        ExcelUtil<IpRangeExcelVO> excelUtil = new ExcelUtil<>(IpRangeExcelVO.class);
        List<IpRangeExcelVO> collect = dataInfo.getRows().stream()
                .filter(Objects::nonNull)
                .map(IpRange.class::cast)
                .map(ipRange -> {
                    IpRangeExcelVO bean = BeanUtil.toBean(ipRange, IpRangeExcelVO.class);
                    // TODO 设置序号
                    bean.setIndex(null);
                    return bean;
                })
                .collect(Collectors.toList());
        excelUtil.easyExcelExport(response, collect, "sheet", "IP段数据导出", IpRangeExcelVO.class);
    }

    /**
     * 通过Id查询网段
     *
     * @param rangeId 网段ID
     * @return IpRange 网段实体
     */
    @Override
    public IpRange findById(Long rangeId) {
        return getBaseMapper().selectById(rangeId);
    }

    /**
     * 通过ID查询网段集合
     *
     * @param rangeIds 网段ID集合
     * @return List<IpRange> 网段集合
     */
    @Override
    public List<IpRange> findByIds(List<Long> rangeIds) {
        if (CollUtil.isEmpty(rangeIds)) {
            return Collections.emptyList();
        }
        return getBaseMapper().selectBatchIds(rangeIds);
    }

    /**
     * 通过网段名称获取网段集合
     *
     * @param strings 网段名称集合
     * @return Map<String, IpRange> 网段集合
     */
    @Override
    public Map<String, IpRange> findByRangeNames(Collection<String> strings) {
        if (CollUtil.isEmpty(strings)) {
            return Collections.emptyMap();
        }
        return getBaseMapper().selectList(Wrappers.<IpRange>lambdaQuery()
                .select(IpRange::getRangeName, IpRange::getId)
                .in(IpRange::getRangeName, strings)
        ).stream().collect(Collectors.toMap(IpRange::getRangeName, Function.identity(), (k1, k2) -> k2));
    }
}
